package com.seleniti.enpareja.web.desktop.client.ui.view;

import static com.google.gwt.dom.client.BrowserEvents.CLICK;

import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.ioc.commons.impl.gwt.client.ui.HasEnabledWrapper;
import org.ioc.commons.impl.gwt.client.ui.HasTextWrapper;
import org.ioc.commons.impl.gwt.client.ui.HasValueWrapper;
import org.ioc.commons.impl.gwt.client.ui.ViewComposite;
import org.ioc.commons.impl.gwt.shared.common.util.WrappedDeckPanelAsEnum;
import org.ioc.commons.ui.HasText;
import org.ioc.commons.ui.HasValue;
import org.ioc.commons.ui.impl.HasValueAdapter;
import org.ioc.commons.utils.Format;
import org.ioc.commons.utils.FormatterLogger;

import com.google.gwt.cell.client.AbstractSafeHtmlCell;
import com.google.gwt.cell.client.ValueUpdater;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.EventTarget;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat;
import com.google.gwt.resources.client.CssResource;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
import com.google.gwt.text.shared.AbstractSafeHtmlRenderer;
import com.google.gwt.text.shared.SafeHtmlRenderer;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.cellview.client.CellList;
import com.google.gwt.user.client.ui.DeckLayoutPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.Html5DateBox;
import com.google.gwt.user.client.ui.Html5TextBox;
import com.google.gwt.user.client.ui.ToggleButton;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.datepicker.client.CalendarUtil;
import com.google.gwt.view.client.ListDataProvider;
import com.google.gwt.view.client.NoSelectionModel;
import com.google.gwt.view.client.ProvidesKey;
import com.google.gwt.view.client.SelectionChangeEvent;
import com.seleniti.enpareja.controller.common.domain.LoggedUserInfo;
import com.seleniti.enpareja.controller.model.integration.database.domain.Food;
import com.seleniti.enpareja.controller.ui.view.MealsView;
import com.seleniti.enpareja.controller.ui.view.MealsView.Action;
import com.seleniti.enpareja.controller.ui.view.MealsView.Event;
import com.seleniti.enpareja.controller.ui.view.MealsView.Operation;
import com.seleniti.enpareja.controller.ui.view.MealsView.Presenter;
import com.seleniti.enpareja.controller.ui.view.MealsViewPresenter;
import com.seleniti.enpareja.web.desktop.client.ui.cell.EnParejaTemplate;
import com.seleniti.enpareja.web.desktop.client.ui.resources.CommonResources;
import com.seleniti.enpareja.web.desktop.client.ui.widget.Checker;
import com.seleniti.enpareja.web.desktop.client.ui.widget.Checker.SelectionMode;

public class MealsViewWidget extends EnParejaBaseView<Event, Operation, Action, Presenter> implements MealsView {

	private static final FormatterLogger logger = FormatterLogger.getLogger(MealsViewWidget.class);

	private static TemplateUiBinder uiBinder = GWT.create(TemplateUiBinder.class);

	interface TemplateUiBinder extends UiBinder<Widget, MealsViewWidget> {
	}

	private static EnParejaTemplate template = GWT.create(EnParejaTemplate.class);

	public static interface Style extends CssResource {
		String container();

		String columnsHeader();

		String dialog();

		String today();

		String lunch();

		String dinner();

	}

	@UiField(provided = true)
	CellList<Food> cellList;

	@UiField
	Style style;

	@UiField
	ToggleButton btnSwitch;

	@UiField
	ToggleButton btnDelete;

	@UiField
	CommonResources common;

	@UiField(provided = true)
	Checker<Food> singleChecker;

	@UiField(provided = true)
	Checker<Food> doubleChecker;

	@UiField
	VerticalPanel doneSettingsPanel;

	@UiField
	Html5DateBox date;

	@UiField
	Html5TextBox lunch;

	@UiField
	Html5TextBox dinner;

	@UiField
	Html5DateBox filterFrom;

	@UiField
	Html5DateBox filterTo;

	@UiField
	DeckLayoutPanel mainPanel;

	private ListDataProvider<Food> listDataProvider;

	private boolean loading;

	private ProvidesKey<Food> keyProvider;

	private ActionMode actionMode;

	private boolean enabled = true;

	private WrappedDeckPanelAsEnum<Board> boardManager;

	public MealsViewWidget() {
		this.presenter = new MealsViewPresenter(this, eventbus, operationManager, actionController);

		this.keyProvider = new ProvidesKey<Food>() {

			@Override
			public Object getKey(Food item) {
				return item != null ? item.getFoodId() : null;
			}
		};
		SafeHtmlRenderer<Food> renderer = new AbstractSafeHtmlRenderer<Food>() {

			@Override
			public SafeHtml render(Food object) {
				if (object != null) {
					SafeHtmlBuilder shb = new SafeHtmlBuilder();

					DateTimeFormat dtf = DateTimeFormat.getFormat(PredefinedFormat.DATE_FULL);
					boolean today = object.getDate() != null ? CalendarUtil.isSameDate(new Date(), object.getDate())
							: false;

					String header = object.getDate() != null ? (today ? "<b>HOY </b>" : "")
							+ Format.capitalize(dtf.format(object.getDate())) : "-- sin fecha --";
					String lunchStr = object.getLunch() != null ? object.getLunch() : "";
					String dinnerStr = object.getDinner() != null ? object.getDinner() : "";

					shb.append(template.div(common.style().columnHeader(), SafeHtmlUtils.fromTrustedString(header)));
					shb.append(template.hImgDiv(common.sunIcon().getSafeUri(), style.lunch(),
							SafeHtmlUtils.fromString(lunchStr)));
					shb.append(template.hImgDiv(common.moonIcon().getSafeUri(), style.dinner(),
							SafeHtmlUtils.fromString(dinnerStr)));

					return template.listElement(today ? style.today() : "", shb.toSafeHtml());
				} else {
					return SafeHtmlUtils.fromString("-- no element --");
				}
			}
		};
		this.cellList = new CellList<Food>(new AbstractSafeHtmlCell<Food>(renderer, "click") {

			@Override
			protected void render(com.google.gwt.cell.client.Cell.Context context, SafeHtml data, SafeHtmlBuilder sb) {
				if (data != null) {
					sb.append(data);
				}
			}

			public void onBrowserEvent(Context context, Element parent, Food value, NativeEvent event,
					ValueUpdater<Food> valueUpdater) {
				super.onBrowserEvent(context, parent, value, event, valueUpdater);
				if (CLICK.equals(event.getType())) {
					EventTarget eventTarget = event.getEventTarget();
					if (!Element.is(eventTarget)) {
						return;
					}
					if (parent.getFirstChildElement().isOrHasChild(Element.as(eventTarget))) {
						// Ignore clicks that occur outside of the main element.
						if (valueUpdater != null) {
							valueUpdater.update(value);
						}
					}
				}
			}

		}, keyProvider);

		this.cellList.setPageSize(Integer.MAX_VALUE);

		this.cellList.setValueUpdater(new ValueUpdater<Food>() {

			@Override
			public void update(Food value) {
				if (getActionMode() == ActionMode.MODIFY_ELEMENT) {
					logger.info("Clicked meal {0}", value);
					showModifyForm(value);
				}
			}
		});

		HTML empty = new HTML("Ningún elemento en tu lista");
		this.cellList.setEmptyListWidget(empty);

		this.cellList.setSelectionModel(new NoSelectionModel<Food>(keyProvider));

		this.listDataProvider = new ListDataProvider<Food>();
		this.listDataProvider.addDataDisplay(cellList);

		this.singleChecker = new Checker<Food>(105, 1);
		this.singleChecker.addDataDisplay(cellList);

		this.doubleChecker = new Checker<Food>(105, 2);
		this.doubleChecker.addDataDisplay(cellList);

		initWidget(uiBinder.createAndBindUi(this));

		empty.setStyleName(common.style().emptyList());

		this.boardManager = new WrappedDeckPanelAsEnum<Board>(mainPanel, Board.values());
	}

	@Override
	public List<Food> foodsList() {
		return this.listDataProvider.getList();
	}

	@UiHandler("btnCancel")
	void onCancelButtonClicked(ClickEvent evt) {
		this.hideModificationForm();
	}

	@UiHandler("btnOk")
	void onOkButtonClicked(ClickEvent evt) {
		this.saveModificationForm();
	}

	@UiHandler("filterFrom")
	void onFilterFromValueChanged(ValueChangeEvent<Date> evt) {
		this.presenter.onDateFilterChanged();
	}

	@UiHandler("filterTo")
	void onFilterToValueChanged(ValueChangeEvent<Date> evt) {
		this.presenter.onDateFilterChanged();
	}

	@UiHandler("btnSwitch")
	void onSwitchButtonToggled(ValueChangeEvent<Boolean> evt) {
		boolean enabled = Boolean.TRUE.equals(evt.getValue());
		this.setActionMode(enabled ? ActionMode.SWITCH_MEALS : ActionMode.MODIFY_ELEMENT);

		if (enabled) {
			btnDelete.setDown(false);
		}
	}

	@UiHandler("btnDelete")
	void onDeleteButtonToggled(ValueChangeEvent<Boolean> evt) {
		boolean enabled = Boolean.TRUE.equals(evt.getValue());
		this.setActionMode(enabled ? ActionMode.DELETE_ELEMENT : ActionMode.MODIFY_ELEMENT);

		if (enabled) {
			btnSwitch.setDown(false);
		}
	}

	@UiHandler("singleChecker")
	void onSingleCheckerSelectionChanged(SelectionChangeEvent evt) {
		presenter.onSingleSelectionChanged();
	}

	@UiHandler("doubleChecker")
	void onDoubleCheckerSelectionChanged(SelectionChangeEvent evt) {
		presenter.onDoubleSelectionChanged();
	}

	@Override
	public void setLoading(boolean loading) {
		this.loading = loading;
		btnSwitch.setEnabled(!loading);
		btnDelete.setEnabled(!loading);
	}

	@Override
	public boolean isLoading() {
		return this.loading;
	}

	@Override
	public void setActionMode(ActionMode mode) {
		this.actionMode = mode;
		switch (mode) {
		case DELETE_ELEMENT:
			singleChecker.setSelectionMode(SelectionMode.Multiselection);
			singleChecker.setVisible(true);
			doubleChecker.setVisible(false);
			btnDelete.setDown(true);
			btnSwitch.setDown(false);
			break;
		case MODIFY_ELEMENT:
			singleChecker.setSelectionMode(SelectionMode.SingleSelection);
			singleChecker.setVisible(false);
			doubleChecker.setVisible(false);
			btnDelete.setDown(false);
			btnSwitch.setDown(false);
			break;
		case SWITCH_MEALS:
			doubleChecker.setSelectionMode(SelectionMode.Multiselection);
			singleChecker.setVisible(false);
			doubleChecker.setVisible(true);
			btnDelete.setDown(false);
			btnSwitch.setDown(true);
			break;
		}

		this.presenter.onActionModeChanged();
	}

	@Override
	public ActionMode getActionMode() {
		return actionMode;
	}

	@Override
	public Set<Food> getSingleSelection() {
		Set<Food> selection = new HashSet<Food>();
		boolean selected[] = this.singleChecker.getSelection();
		for (int i = 0; i < selected.length; i++) {
			boolean elmSelected = selected[i];

			if (elmSelected) {
				selection.add(this.foodsList().get(i));
			}
		}

		return selection;
	}

	@Override
	public Set<DoubleSelection> getDoubleSelection() {
		Set<DoubleSelection> selection = new HashSet<DoubleSelection>();
		boolean selected[] = this.doubleChecker.getSelection();
		for (int i = 0; i < selected.length; i++) {
			boolean elmSelected = selected[i];

			if (elmSelected) {
				Food food = this.foodsList().get(i / 2);
				DoubleSelection ds = new DoubleSelection();
				ds.setElement(food);
				ds.setDinner(i % 2 == 1);
				selection.add(ds);
			}
		}

		return selection;
	}

	@Override
	public boolean isEnabled() {
		return this.enabled;
	}

	@Override
	public void setEnabled(boolean enabled) {
		this.enabled = true;
		HasEnabledWrapper.setEnabled(enabled, btnDelete, btnSwitch);
		this.setTitle(enabled ? "" : "¡Asociate a tu pareja antes de empezar a usar todas las opciones!");
	}

	@Override
	public HasValue<Date> date() {
		return HasValueWrapper.from(date, true, this);
	}

	@Override
	public void saveModificationForm() {
		this.presenter.onMealModified();
	}

	@Override
	public void showModifyForm(Food element) {
		// this.element = element;
		this.date.setValue(element.getDate());
		this.lunch.setValue(element.getLunch());
		this.dinner.setValue(element.getDinner());
		this.boardManager.setValue(Board.EDIT_FORM);
		// this.formDialog.center();
	}

	@Override
	public void hideModificationForm() {
		// this.formDialog.hide();
		this.boardManager.setValue(Board.MAIN_LIST);
	}

	@Override
	public HasValue<Date> filterToDate() {
		return HasValueWrapper.from(filterTo, true, this);
	}

	@Override
	public HasValue<Date> filterFromDate() {
		return HasValueWrapper.from(filterFrom, true, this);
	}

	@Override
	public HasText dinner() {
		return HasTextWrapper.from(dinner, this);
	}

	@Override
	public HasText lunch() {
		return HasTextWrapper.from(lunch, this);
	}

	@Override
	public HasValue<Board> boardShown() {
		return this.boardManager;
	}

	@Override
	public boolean goBoardBack() {
		return this.presenter.onGoBoardBack();
	}

	@Override
	public HasValue<LoggedUserInfo> loggedUserInfo() {
		return HasValueAdapter.<LoggedUserInfo>forField("loggedUserInfo", this);
	}
}
